package com.justgame.project.generator.boot.impl;


import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.justgame.project.cloud.base.entity.BaseEntity;
import com.justgame.project.cloud.base.entity.BaseTable;
import com.justgame.project.cloud.base.entity.StringEncrypt;
import com.justgame.project.generator.boot.AbstractDbGenerator;
import com.justgame.project.generator.boot.IAutoGenerator;
import com.justgame.project.generator.container.EntityContainer;
import com.justgame.project.generator.enums.TemplateType;
import com.justgame.project.generator.properties.GlobalProperties;
import com.justgame.project.generator.schema.Entity;
import com.justgame.project.generator.template.ITemplateResolve;
import com.justgame.project.generator.template.TemplateResolveManager;
import com.justgame.project.generator.utils.FreemarkerInnerUtils;
import com.justgame.project.generator.utils.NameUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * MySQL数据源代码生成器
 */
@Log4j2
@Component
@ConditionalOnProperty(
        prefix = "spring.datasource",
        name = "driver-class-name",
        havingValue = "com.mysql.cj.jdbc.Driver"
)
public class MySqlCodeGeneratorImpl extends AbstractDbGenerator implements IAutoGenerator {
    public final String baseDir = "/src/main/java";

    private final GlobalProperties globalProperties;
    private final EntityContainer entityContainer;
    private final DataSourceProperties dataSourceProperties;
    private final TemplateResolveManager templateResolveManager;

    public MySqlCodeGeneratorImpl(GlobalProperties globalProperties, EntityContainer entityContainer, DataSourceProperties dataSourceProperties, TemplateResolveManager templateResolveManager) {
        this.globalProperties = globalProperties;
        this.entityContainer = entityContainer;
        this.dataSourceProperties = dataSourceProperties;
        this.templateResolveManager = templateResolveManager;
    }

    @Override
    public GlobalConfig buildGlobalConfig(){
        Assert.notNull(globalProperties,"GlobalProperties is Null, please fill it!");
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setFileOverride(true);//文件重写覆盖（可能会造成代码丢失）
        globalConfig.setAuthor(globalProperties.getAuthor());
        globalConfig.setOpen(false); // 生成代码后是否自动打开文件夹
        return globalConfig;
    }

    @Override
    public DataSourceConfig buildDataSourceConfig(){
        Assert.notNull(dataSourceProperties,"DataSourceProperties is Null, please fill it!");
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl(dataSourceProperties.getUrl());
        dataSourceConfig.setDriverName(dataSourceProperties.getDriverClassName());
        dataSourceConfig.setUsername(dataSourceProperties.getUsername());
        dataSourceConfig.setPassword(dataSourceProperties.getPassword());
        dataSourceConfig.setDbType(DbType.MYSQL);
        dataSourceConfig.setTypeConvert(new MySqlTypeConvert() {
            @Override
            public IColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
                if ( fieldType.toLowerCase().contains( "tinyint" ) ) {
                    return DbColumnType.BOOLEAN;
                }
                if ( fieldType.toLowerCase().contains( "datetime" ) ) {
                    return DbColumnType.LOCAL_DATE_TIME;
                }
                //if (fieldType.toLowerCase().contains("text")){
                //    return DbColumnType.CLOB;
                //}
                return (DbColumnType) super.processTypeConvert(globalConfig, fieldType);
            }
        });
        return dataSourceConfig;
    }

    @Override
    public PackageConfig buildPackageConfig(Entity entity){
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.justgame.project.service");
        pc.setModuleName(entity.getModuleName());
        return pc;
    }

    @Override
    public TemplateConfig buildTemplateConfig(){
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setXml(null);
        return templateConfig;
    }

    @Override
    public InjectionConfig buildInjectionConfig(Entity entity) {
        InjectionConfig injectionConfig = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String,Object> paramsMap = new HashMap<>();
                paramsMap.put("applicationNameFormat",globalProperties.getApplicationNameFormat());
                paramsMap.put("formatSymbol",globalProperties.getFormatSymbol());
                paramsMap.put("parent","com.justgame.project.service");
                paramsMap.put("SuperServiceImplPackage","com.justgame.project.base.servcice.BasicServiceImpl");
                paramsMap.put("SuperServiceImplName","BasicServiceImpl");
                paramsMap.put("tool",new FreemarkerInnerUtils());
                paramsMap.put("encryptFieldClass", StringEncrypt.class);
                paramsMap.put("entity", entity);
                this.setMap(paramsMap);
            }
        };
        List<FileOutConfig> focList = new ArrayList<>();
        for (TemplateType type : TemplateType.values()) {
            ITemplateResolve resolve = templateResolveManager.getResolve(type);
            boolean isExist = new File(resolve.getOutputFilePath(entity.getTableName())).exists();
            if (isExist && !entityContainer.needCover(type)){
                continue;
            }
            FileOutConfig foc = resolve.buildConfig();
            Assert.notNull(foc,"无效的模板配置信息");
            focList.add(foc);
        }
        injectionConfig.setFileOutConfigList(focList);
        return injectionConfig;
    }

    @Override
    protected StrategyConfig buildStrategyConfig(Entity entity) {
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setEntitySerialVersionUID(false);
        Assert.notNull(entity.getEntityType(),"实体： "+entity.getName()+" 未设置实体类型");
        switch (entity.getEntityType()){
            case BASE_ENTITY:
                strategy.setSuperEntityClass(BaseEntity.class);
                break;
            case BASE_TABLE:
                strategy.setSuperEntityClass(BaseTable.class);
                break;
            default:
                throw new RuntimeException("支持此实体类型");
        }
        strategy.setInclude(entity.getTableName());
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(""); // 设置表前缀
        return strategy;
    }

    @Override
    protected AbstractTemplateEngine buildTemplateEngine() {
        return new FreemarkerTemplateEngine();
    }

    @Override
    public final void execute(String[] tableNames){
        DataSourceConfig dataSourceConfig = buildDataSourceConfig();
        GlobalConfig globalConfig = buildGlobalConfig();
        TemplateConfig templateConfig = buildTemplateConfig();
        AbstractTemplateEngine templateEngine = buildTemplateEngine();
        for (String tableName : tableNames) {
            try{
                log.info("正在自动生成代码："+tableName);
                Entity entity = entityContainer.findEntityByTableName(tableName).orElse(null);
                Assert.notNull(entity,"未找到对应的实体");
                AutoGenerator autoGenerator = new AutoGenerator();
                autoGenerator.setDataSource(dataSourceConfig);
                autoGenerator.setGlobalConfig(globalConfig);
                autoGenerator.setTemplate(templateConfig);
                autoGenerator.setTemplateEngine(templateEngine);
                autoGenerator.setCfg(buildInjectionConfig(entity));
                autoGenerator.setStrategy(buildStrategyConfig(entity));
                autoGenerator.setPackageInfo(buildPackageConfig(entity));
                autoGenerator.execute();
            }catch (Exception e){
                log.error("生成 "+NameUtils.getEntityNameByTableName(tableName)+" 模板代码时发生异常：",e);
                System.exit(1);
            }
        }
    }

    @Override
    public DbType dbType() {
        return DbType.MYSQL;
    }
}
